﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using System.Web.Http;
using SpreadsheetGear;
using Sustainalytics.ClientDW.Entities;
using Sustainalytics.Controversies.BusinessLogic;
using Sustainalytics.Entities;
using Sustainalytics.Entities.ConnectionStrings;
using Sustainalytics.EsgRatings.BusinessLogic;
using Sustainalytics.ESGRatings.BusinessLogic;
using Sustainalytics.Utils;
using Sustainalytics.Utils.WebApi.ErrorCodes;
using System.Web.Http.Cors;

namespace Sustainalytics.ESGRatings.Service.Controllers
{
    using Sustainalytics.Entities.ESGRatings;    

    [EnableCors(origins: "*", headers: "*", methods: "*")]
    [RoutePrefix("api/v1/esgfocusthreshold")]
    [LogControllerFilter(true,true,false)]
    public class EsgFocusAdminController:ApiController
    {

        [Route("")]
        public async Task<HttpResponseMessage> Post()
        {
            try
            {
                if (!Request.Content.IsMimeMultipartContent())
                {
                    return Request.CreateResponse(HttpStatusCode.UnsupportedMediaType);
                }
                
                var provider = GetMultipartProvider();
               var result= await Request.Content.ReadAsMultipartAsync(provider);
                var dataFile = File.ReadAllBytes(result.FileData.FirstOrDefault().LocalFileName);
                var isPercentile = false;
                if (result.FormData.HasKeys())
                {
                    if (result.FormData.AllKeys.Contains("isPercentile"))
                    {
                        bool.TryParse(result.FormData.Get("isPercentile"), out isPercentile);
                    }
                }
                if (!isPercentile)
                {
                    var dict = new Dictionary<int, string>()
                    {
                        {0, "Laggard"},
                        {1, "Underperformer"},
                        {2, "Average Performer"},
                        {3, "Outperformer"},
                        {4, "Leader"},
                    };
                    return ProcesExcelThresholds<EsgFocusThresholds,string>(dataFile,dict,5);    
                }
                var dictVal = new Dictionary<int, float>()
                {
                    {0, (float) 0.05},
                    {1, (float) 0.1},
                    {2, (float) 0.15},
                    {3, (float) 0.2},
                    {4, (float) 0.25},
                    {5, (float) 0.3},
                    {6, (float) 0.35},
                    {7, (float) 0.4},
                    {8, (float) 0.45},
                    {9, (float) 0.5},
                    {10, (float) 0.55},
                    {11, (float) 0.6},
                    {12, (float) 0.65},
                    {13, (float) 0.7},
                    {14, (float) 0.75},
                    {15, (float) 0.8},
                    {16, (float) 0.85},
                    {17, (float) 0.9},
                    {18, (float) 0.95}
                };

                return ProcesExcelThresholds<EsgFocusPercentileThreshold, float>(dataFile, dictVal, dictVal.Count);
            }
            catch (Exception ex)
            {
                EsgRatingsEventSource.Log.LogException(ex);
                return Request.CreateResponse(HttpStatusCode.InternalServerError,
                    new {ErrorStatusCode = WebAPIGeneralErrorCodes.UnhandledException});
            }
        }

        private HttpResponseMessage ProcesExcelThresholds<T,TU>(byte[] dataFile,Dictionary<int,TU> values,int columns) where T : EsgThreshold<TU>,IEntity,new()
        {

            //var excelParser = new ExcelParser<T, TU>(new Dictionary<int, string>()
            //{
            //    {0, "Industry Laggard"},
            //    {1, "Underperformer"},
            //    {2, "Average Performer"},
            //    {3, "Outperformer"},
            //    {4, "Industry Leader"},
            //}, 5);
            var excelParser=new ExcelParser<T,TU>(values,columns);

            List<T> listThresholds;
            if (!excelParser.TryParse(dataFile, out listThresholds))
            {
                var strData = excelParser.ErrorLog.Any()
                    ? excelParser.ErrorLog.Aggregate((a, b) => string.Format("{0}{1}{2}", a, Environment.NewLine, b))
                    : "There was an error on parsing excel data";

                return Request.CreateResponse(HttpStatusCode.InternalServerError,
                    new {Errors = strData});
            }
            var thresholdsManager =new EsgThresholdManager<T,TU>(PlainBlFactory.GetConnectionString(ConnectionStringType.ClientDW));
            var thresholdId = Guid.NewGuid();
            var dateUpload = DateTime.Now;

            foreach (var esgFocusThresholdse in listThresholds)
            {
                esgFocusThresholdse.ThresholdGroupId = thresholdId;
                esgFocusThresholdse.UploadTime = dateUpload;
            }

            if (!thresholdsManager.SetupNewThresholds(listThresholds))
            {
                return Request.CreateResponse(HttpStatusCode.InternalServerError,
                    new {Status = "An error occured on mongo writing"});
            }

            return Request.CreateResponse(HttpStatusCode.OK);
        }


        private MultipartFormDataStreamProvider GetMultipartProvider()
        {
            var uploadFolder = @"TempFileUploads";
            var root = uploadFolder;
            Directory.CreateDirectory(root);
            return new MultipartFormDataStreamProvider(root);
        }
        
    }


    public class ExcelParser<T,TU> where T:EsgThreshold<TU>,IEntity, new()
    {
        private readonly IPlainBl<PeerGroup> _subIndustry;
        private readonly List<string> _logs;

        //private readonly IDictionary<int, string> _valuesStr = new Dictionary<int, string>()
        //{
        //    {0,"Industry Laggard"},
        //    {1,"Underperformer"},
        //    {2,"Average Performer"},
        //    {3,"Outperformer"},
        //    {4,"Industry Leader"},
        //}; 


        

        private readonly IDictionary<int, TU> _valuesStr;
        private int _prev;
        private readonly int _max;
        private readonly int _columnCount;
        private int _rowBegin;


        public ExcelParser(IDictionary<int, TU> valuesStr,int columCount)
        {
            _valuesStr = valuesStr;
            _subIndustry = PlainBlFactory.GetInstance<PeerGroup>();
            _logs = new List<string>();
            _prev = 0;
            _max = 101;
            _columnCount = columCount;
            _rowBegin = 2;
        }

        public IEnumerable<string> ErrorLog
        {
            get { return _logs; }
        }


        public bool TryParse(byte[] arrayData, out List<T> thresholds)
        {
            thresholds = new List<T>();

            SpreadsheetGear.IWorkbook book = SpreadsheetGear.Factory.GetWorkbookSet()
                .Workbooks.OpenFromMemory(arrayData);
            var cellData = book.Worksheets[0].Cells;
            while (true)
            {
                var data = cellData[_rowBegin, 0].Text;
                if (string.IsNullOrEmpty(data)) break;
                var found = _subIndustry.ReadAllWhere(a => a.Name == data).FirstOrDefault();
                if (found == null)
                {
                    _logs.Add(string.Format("Peer group:{0} not found in SustainalyticsIndustryGroup", data));
                    _rowBegin++;
                    continue;
                }
                var output = ProcessRow(_columnCount, cellData, _rowBegin, found);
                thresholds.AddRange(output);
                _rowBegin++;

            }
            return !ErrorLog.Any();
        }

        private T[] ProcessRow(int columnCount, IRange cellData, int rowBegin,PeerGroup found)
        {
            var rowThresholds = new T[columnCount];
            _prev = 0;
            var i = 1;
            while (true)
            {
                var currentIndex = i-1;
                if (i == columnCount)
                {
                    rowThresholds[currentIndex] = CreateRowThreshold(found, currentIndex, _prev, _max);
                    break;
                }

                var valueData = cellData[rowBegin, i].Text;
                if (string.IsNullOrEmpty(valueData))
                {
                    _logs.Add(string.Format("The data for column {0} is null at peer group:{1}", i,found.Name));
                    i++;
                    continue;
                }
                int parse;
                double doubleParse;
                if (!double.TryParse(valueData, out doubleParse))
                {
                    _logs.Add(string.Format("The data for column {0} is invalid for parse at peer group:{1}", i,found.Name));
                    i++;
                    continue;
                }
                parse = (int) Math.Round(doubleParse);
                rowThresholds[currentIndex]=CreateRowThreshold(found, currentIndex, _prev, parse);
                _prev = parse;
                i++;
            }

            ValidateRowThresholds(rowThresholds);
            return rowThresholds;
        }

        private T CreateRowThreshold(PeerGroup found, int currentIndex, double prev, double parse)
        {
            return new T()
            {
                Id = Guid.NewGuid(),
                PeerGroupId = found.Id,
                PeerGroup = found.Name,
                Value = _valuesStr[currentIndex],
                LowerBound = prev,
                UpperBound = parse
            };
        }

        private void ValidateRowThresholds(T[] rowThresholds)
        {
            
            var invalidBounds = false;
            foreach (var esgFocusThresholdse in rowThresholds)
            {
                if (esgFocusThresholdse == null) continue;
                if (esgFocusThresholdse.LowerBound > esgFocusThresholdse.UpperBound)
                {
                    invalidBounds = true;
                    _logs.Add(
                        string.Format(
                            "The peer group {0} at:{1} classification has invalid bounds lower:{2} upper:{3}",
                            esgFocusThresholdse.PeerGroup, esgFocusThresholdse.Value, esgFocusThresholdse.LowerBound,
                            esgFocusThresholdse.UpperBound));
                }
            }
            if (invalidBounds) return;
            var found = rowThresholds.FirstOrDefault(a => a != null);
            if (found == null) return;
            var prev = found.UpperBound;

            for (int i = 1; i < rowThresholds.Length; i++)
            {
                if (rowThresholds[i] == null) continue;
                if (prev > rowThresholds[i].LowerBound || prev > rowThresholds[i].UpperBound)
                {
                    _logs.Add(string.Format("Invalid order of bounds at peer group:{0}",rowThresholds[i].PeerGroup));
                    break;
                }
            }
        }
    }
}
